#pragma once
#include "../Net.hpp"
#include "../Message.hpp"
#include <future>
/*
模块说明：
如果客户端是多线程发送不同的请求给服务端，而客户端只有一个epoll线程去监听套接字的就绪事件
然后调用回调函数进行回应的处理，如果不描述组指这些发送的请求，那么接收到的回应也不知道是哪个线程发送的
请求，所有该模块就是提供管理每个客户端线程发送的请求的和处理回应的
--具体的功能：底层提供两个发送请求的接口，当调用该接口时会根据 请求发送时对应的回应的处理方式比如当前
请求的回应是基于回调函数处理的，客户端epoll线程接收到回应时经过协议层对象处理得到一个Message对象，然后交给该模块
处理，该模块根据该Messgae对象的uuid去查找提前注册好的请求描述对象，然后调用该描述对象里面的回调函数进行处理该Message对象。
 请求发送时对应的回应的处理方式即请求描述对象会提前注册好的。

*/

namespace Client
{
    using RequestCallback = std::function<void(const Abstract::BaseMessage::MesaPtr&)>;
    using AsynResponce = std::future<Abstract::BaseMessage::MesaPtr>; // 基于future对象获取回应
    class Resquestor
    {
    public:
        using RPtr=std::shared_ptr<Resquestor>;
        // 定义一个内部类来描述组指客户端的请求
        struct Request_Describe
        {
            using RequestDesPtr = std::shared_ptr<Request_Describe>;
            Abstract::BaseMessage::MesaPtr request;                // 基类指针管理不同的请求对象
            CliRpcType _Ctype;                                     // 客户端请求方式描述字段--同步还是异步请求
            std::promise<Abstract::BaseMessage::MesaPtr> Responce; // 异步请求方式通过promise对象获取回应
            RequestCallback _callback;                             // 拿到响应基于回调函数去处理响应
        };
        // 处理回应的函数
        void DealResponce(const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &ResPtr)
        {
            //std::unique_lock<std::mutex> lock(_mutex);
            std::string uuid = ResPtr->GetUuid();
            auto DescResq_Ptr = GetDescResq(uuid); // 获取对应的请求描述
            if (DescResq_Ptr.get() == nullptr)
            {
                ELOG("该uuid:%s回应没有对应的请求描述", uuid.c_str());
                return;
            }
            // 如果该回应对应的请求是异步请求--删除Rpc请求的描述对象的工作统一交给发送Rpc的线程去处理
            //因为异步请求发送 需要通过描述对象中的promise对象和回调函数获取回应的结果
            //而回调函数对象中也有promise的智能指针对象，不能让监听线程去销毁promise对象
            //不然发送请求的线程就不能通过对应的promise对象获取结果了
            if (DescResq_Ptr->_Ctype == CliRpcType::REQ_UYSNC)
            {
                DescResq_Ptr->Responce.set_value(ResPtr); // 设置回应到指定的线程中的promise对象中然后由发送的异步线程进行处理、
                // 不要设置好Rpc回应到对应的promise对象后就马上删除这个Rpc请求的描述对象
                // 因为发送线程可能还没有把promise中的回应读取走做处理，把这个删除工作交给发送线程去完成
                // 不要给监听线程做
            }
            else if (DescResq_Ptr->_Ctype == CliRpcType::REQ_CALLBACK)
            {
                // 接收的回应交给回调函数进行处理
                DescResq_Ptr->_callback(ResPtr);
                // 处理回应完成之后删除对应的请求描述对象
                //_DescResTables.erase(uuid);
            }
            else
            {
                ELOG("接收到的回应对应的请求类型未知");
            }
            // 处理回应完成之后删除对应的请求描述对象
            //_DescResTables.erase(uuid);
        }
        // 基于future对象，即异步模式处理回应的方式去发送请求
        bool Send(const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr, AsynResponce &aync_rsp
        ,std::string& uuid)
        {
            Request_Describe::RequestDesPtr ResDesPtr = NewDescResq(Mptr, CliRpcType::REQ_UYSNC);
            if (ResDesPtr.get() == nullptr)
            {
                ELOG("构造描述请求对象失败");
                return false;
            }
            Con->Send(Mptr);//发送请求
            aync_rsp = ResDesPtr->Responce.get_future(); // 异步线程获取future对象，将来客户端的epoll线程监听并获取
            // 回应，把获取的回应设置到promise对象中，然后异步线程通过future对象获取。
            uuid=ResDesPtr->request->GetUuid();//把删除请求描述对象的
            return true;
        }
        // 基于回调函数异步（promise对象报错回应结果，发送线程通过future对象获取回应）处理回应的方式去发送请求
        bool Send(const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr, std::string& uuid,const RequestCallback &Rbc
        )
        {
            Request_Describe::RequestDesPtr ResDesPtr = NewDescResq(Mptr, CliRpcType::REQ_CALLBACK, Rbc);
            if (ResDesPtr.get() == nullptr)
            {
                ELOG("构造描述请求对象失败");
                return false;
            }

            uuid=ResDesPtr->request->GetUuid();
            Con->Send(Mptr);//发送请求
            return true;
        }

        // 基于发送线程同步等待请求结果的发送方式，在发送完请求和阻塞等待返回结果
        bool Send(const Abstract::BaseConnection::ConPtr &Con, const Abstract::BaseMessage::MesaPtr &Mptr, Abstract::BaseMessage::MesaPtr &ResPtr)
        {
            Request_Describe::RequestDesPtr ResDesPtr = NewDescResq(Mptr, CliRpcType::REQ_UYSNC);
            if (ResDesPtr.get() == nullptr)
            {
                ELOG("构造描述请求对象失败");
                return false;
            }
           // DLOG("创建发送模块");
            if(Con.get()==nullptr){
                DLOG("发送失败");
            }
            Con->Send(Mptr);//发送请求
            AsynResponce aync_rsp = ResDesPtr->Responce.get_future();

            ResPtr = aync_rsp.get();                 // 阻塞等待获取回应结果，当epoll线程获取消息通过回调函数设置回应
                                                     // 到该异步请求的描述对象的promise对象中去，然后发送请求的线程通过promise对象的future对象获取回应
            _DescResTables.erase(ResPtr->GetUuid()); // 发送线程获取结果后删除对应的描述对象
            return true;
        }

    private:
        // 添加一个客户端请求描述对象
        Request_Describe::RequestDesPtr NewDescResq(const Abstract::BaseMessage::MesaPtr &Mptr, CliRpcType Ctype, RequestCallback callback = RequestCallback())
        {
            std::unique_lock<std::mutex> lock(_mutex);

            Request_Describe::RequestDesPtr RDptr = std::make_shared<Request_Describe>();
            RDptr->request = Mptr;
            RDptr->_Ctype = Ctype;
            // 如果该请求的回应是基于回调函数处理的且回调函数对象不为空
            // 不是的话就设置一个空的回调函数对象
            if (Ctype == CliRpcType::REQ_CALLBACK && callback)
            {
                RDptr->_callback = callback;
            }
            _DescResTables.insert({Mptr->GetUuid(), RDptr});
            return RDptr;
        }
        // 通过uuid获取客户端发送的请求描述对象
        Request_Describe::RequestDesPtr GetDescResq(const std::string uuid)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_DescResTables.count(uuid))
            {
                return _DescResTables[uuid];
            }
            else
            {
                return Request_Describe::RequestDesPtr();
            }
        }

    public:
        void DelDescResq(const std::string& uuid)
        {
            //加锁，多个发送线程可能会同时来删除对应的请求描述对象
            std::unique_lock<std::mutex> lock(_mutex);
            _DescResTables.erase(uuid);
        }

    private:
        std::mutex _mutex;
        std::unordered_map<std::string, Request_Describe::RequestDesPtr> _DescResTables; // 哈希表管理发送的请求uuid为键值key
    };
}